/* Universidad del Valle de Guatemala
 * CC2003 - Algoritmos y Estructuras de Datos
 * HOJA DE TRABAJO #5.1
 * Fecha de Entrega: Jueves 22 de Septiembre de 2011
 */

/**
 * Clase que define las acciones que puede realizar el Banco mediante la implementación de las Colas. 
 * @author José Antonio Bagur - 10275
 */

/* SE IMPORTAN LAS LIBRERÍAS A UTILIZAR EN LA CLASE BANCO */
/* JAVA COLLECTION FRAMEWORK (JCF) */
import java.util.*;

public class Banco {
    
    /* VARIABLES DE INSTANCIA */
    
    public static int tiempoPromedio = 0;
    public static int contadorAtendidos = 0;
    public static int[] tiempoEspera = new int[4];
    public static Cliente[] clientes = new Cliente[300];
    public static AbstractQueue<Cliente>[] colas = new ListaBanco[4];
    
    /* ¡INFORMACIÓN IMPORTANTE!
     * PARA CAMBIAR LA IMPLEMENTACIÓN A LISTA CIRCULAR, MODIFICAR EL SIGUIENTE CÓDIGO:
     * public static AbstractQueue<Cliente> colasalida = new ListaBanco(clientes.length);
     * PARA CAMBIAR LA IMPLEMENTACIÓN A array, MODIFICAR EL SIGUIENTE CÓDIGO:
     * public static AbstractQueue<Cliente> colasalida = new ArrayBanco(clientes.length);
     */
    
    /* ¡INFORMACIÓN IMPORTANTE!
     * IMPLEMENTACIÓN POR MEDIO DE UNA PRIORITYQUEUE
     */
    
    /* ---> */ public static AbstractQueue<Cliente> colasalida3 = new PriorityQueueBanco(clientes.length);
    
    /* MÉTODOS GET */
    
    /**
     * Método que permite obtener el tiempo de espera de los Clientes en el Banco
     * @param i número de Cliento.
     * @return tiempo de espera en el Banco.
     */
    public static int getTiempoEspera(int i) {
        return tiempoEspera[i];
    }
    
    /**
     * Método que permite obtener el tiempo promedio de espera de los Clientes en el Banco.
     * @return tiempo promedio de espera en el Banco.
     */
    public static int getTiempoPromedio() {
        return tiempoPromedio;
    }
    
    /**
     * Método que permite generar aleatoriamente la llegada de los Clientes al Banco.
     * @return tiempo aleatorio de llegada.     
     */
    public static int getLlegada() {
        double n = Math.random()*480 + 1;
        return (int)n;
    }
    
    /**
     * Método que permite generar aleatoriamente el tiempo de espera de los Clientes en el Banco.
     * @return tiempo aleatorio de espera. 
     */
    public static int getEspera() {
        double n = Math.random()*30 + 1;
        return (int)n;
    }
    
    /**
     * Método que permite obtener el estado actual del Banco.
     * @return True si el Banco está lleno y False si el Banco está vacío. 
     */
    public static boolean getBancoLleno() {
        int c = 0;
        for (int i = 0; i < colas.length; i++) {
            if (colas[i].getFull()) 
                c++;
            if (c == colas.length) 
                return true;            
        }
        return false;
    }
    
    /**
     * Método que permite obtener el mejor tiempo de espera de los Clientes en el Banco.
     * @return tiempo de espera en el Banco. 
     */
    public static int getTiempo() {
        int tiempo = 300;
        int indicetiempo = -1;
        
        for (int i =0; i < 4; i++) {
            if (!colas[i].getFull())
                if (getTiempoEspera(i) < tiempo) {
                    tiempo = getTiempoEspera(i);
                    setTiempoPromedio((int)(((getTiempoPromedio() + tiempo)/clientes.length)));
                    indicetiempo = i;
                }
        }
        
        return indicetiempo;
    }
    
    /* MÉTODOS SET */
    
    /**
     * Método que permite definir el tiempo de espera para un determinado Cliente en el Banco.
     * @param i número de Cliente. 
     * @param tiempo de espera en el Banco.
     */
    public static void setTiempoEspera(int i, int tiempo) {
        tiempoEspera[i] = tiempo;
    }
     
    /**
     * Método que permite definir el tiempo promedio de espera en el Banco.
     * @param tiempo de espera promedio en el Banco.
     */
    public static void setTiempoPromedio(int tiempo) {
         tiempoPromedio = tiempo;
    }
    
    /**
     * Método que permite ingresar Clientes a la lista de Eventos manejada por la Cola, implementada por la Lista Cicular o el Array.
     * @param c Cliente ingresado a la Cola. 
     * @throws QueueUnderflow 
     */
    public static void clientesDequeue(int c) throws QueueUnderflow {
        boolean stop = false;
        
        for (int i = 0; i < 4; i++) {
            while(!stop) {
                if (!colas[i].isEmpty()) {
                    if(((colas[i].peek().getEspera()) + (colas[i].peek().getLlegada() + colas[i].peek().getEsperaCola())) < c) {
                        setTiempoPromedio(getTiempoPromedio() + colas[i].peek().getEspera());
                        contadorAtendidos++;
                        tiempoEspera[i] = tiempoEspera[i] - colas[i].peek().getEspera();
                        System.out.println("* - SALIÓ EL CLIENTE " + colas[i].remove());                        
                    } else {
                        stop = true;
                    }                     
                } else {
                    stop = true;
                }   
            } stop = false;
        }
    }
    
    /**
     * Método que permite extraer Clientes a la lista de Eventos manejada por la Cola, implementada por la Lista Cicular o el Array. 
     * @param c Cliente extraído de la Cola. 
     * @throws QueueUnderflow
     * @throws QueueOverflow
     * @throws BancoLleno 
     */
    public static void clientesQueue(Cliente c) throws QueueUnderflow, QueueOverflow, BancoLleno {
        clientesDequeue(c.getLlegada());
        
        if (getBancoLleno())
            throw new BancoLleno("| - LO SENTIMOS, EL BANCO ESTÁ LLENO");
        
        int temporal = getTiempo();
        c.setEsperaCola(getTiempoEspera(temporal));
        colas[temporal].add(c);
        tiempoEspera[temporal] = tiempoEspera[temporal] + c.getEspera();
        System.out.println("* - ENTRÓ EL CLIENTE " + c + " EN LA COLA #" + (temporal+1));
    }  
        
} // FIN DE LA CLASE BANCO 
    
    
    










